iT邦幫忙

2024 iThome 鐵人賽

DAY 27
0
JavaScript

從PM到前端開發:我的React作品集之旅系列 第 27

Day 27: 使用 react-hook-form 高效管理表單提交與驗證

  • 分享至 

  • xImage
  •  

在現代 Web 應用開發中,表單的管理與驗證往往是開發過程中的一大挑戰。上一篇文章中,我們探討了如何追蹤使用者行為並優化應用體驗,而今天,我們的焦點是表單管理。具體來說,我們將深入學習如何使用 react-hook-form 來高效處理 "Let’s Collaborate" 表單,並深入探討它背後的原理與最佳實踐。

https://ithelp.ithome.com.tw/upload/images/20240927/201683306cDioxHhmF.png

為什麼選擇 react-hook-form

在大型應用程式中,表單管理往往是一項重複且容易出錯的工作。React 的受控組件(controlled components)在處理複雜表單時,經常會導致性能問題,例如頻繁重渲染或狀態管理困難。這時,react-hook-form 提供了以下的優勢:

  1. 性能優化:表單以非受控組件(uncontrolled components)為基礎,減少了 React 的重渲染次數,特別是在大型表單中有顯著提升。
  2. 易於集成的表單驗證:內建簡單而強大的表單驗證功能,支持同步和異步驗證。
  3. 精簡的 APIreact-hook-form 提供的 API 如 useForm()register(),讓開發者可以快速管理表單並實現自定義邏輯。
  4. 與其他工具兼容性強:它可以輕鬆與 Yup 等其他驗證庫集成,以進行更複雜的驗證邏輯。

image.png

實際演練

接下來,我們將以 "Let’s Collaborate" 表單為例,展示如何將 react-hook-form 集成到項目中,並結合表單驗證及性能優化進行實作。

Step 1: 安裝 react-hook-form

首先,我們需要安裝 React-Hook-Form:

npm install react-hook-form

安裝 react-hook-form 是實現高效表單管理的第一步,它提供了基本的表單處理功能,後續的驗證和數據處理邏輯都將基於這個庫。

Step 3: 建立 ContactForm 組件

以下是基於 react-hook-form 實現的表單組件代碼,我們將探討其內部機制及設計選擇:

import React from 'react';
import { useForm } from 'react-hook-form';
import * as styles from '@/components/Form/ContactForm.module.scss';

const ContactForm = () => {
    // 使用 useForm hook 來管理表單
    const { register, handleSubmit, formState: { errors } } = useForm();

    // 表單提交處理
    const onSubmit = (data) => {
        console.log('Form submitted:', data);
        // 這裡可以加入 API 提交邏輯
    };

    return (
        <form className={styles.contactForm} onSubmit={handleSubmit(onSubmit)}>
            <div className={styles.formGroup}>
                <label htmlFor="name">Name</label>
                <input
                    type="text"
                    {...register('name', { required: 'Name is required' })}
                    placeholder="Your Name"
                />
                {errors.name && <p className={styles.errorText}>{errors.name.message}</p>}
            </div>
            <div className={styles.formGroup}>
                <label htmlFor="email">Email</label>
                <input
                    type="email"
                    {...register('email', {
                        required: 'Email is required',
                        pattern: {
                            value: /\S+@\S+\.\S+/,
                            message: 'Enter a valid email',
                        },
                    })}
                    placeholder="Your Email"
                />
                {errors.email && <p className={styles.errorText}>{errors.email.message}</p>}
            </div>
            <div className={styles.formGroup}>
                <label htmlFor="serviceItem">Service Items</label>
                <select
                    {...register('serviceItem', { required: 'Please select a service item' })}
                >
                    <option value="">Select a service</option>
                    <option value="design">Design</option>
                    <option value="development">Development</option>
                    <option value="consultation">Consultation</option>
                </select>
                {errors.serviceItem && <p className={styles.errorText}>{errors.serviceItem.message}</p>}
            </div>
            <div className={styles.formGroup}>
                <label htmlFor="projectDetails">Project Details</label>
                <textarea
                    {...register('projectDetails', { required: 'Project details are required' })}
                    placeholder="Describe your project"
                ></textarea>
                {errors.projectDetails && <p className={styles.errorText}>{errors.projectDetails.message}</p>}
            </div>
            <button type="submit" className={styles.submitButton}>
                Submit
                <img src={require(`@/assets/icons/Submit.svg`)} alt="Submit" className={styles.submitIcon} />
            </button>
        </form>
    );
};

export default ContactForm;

說明:

  • 使用 useFormuseForm()react-hook-form 的核心 Hook。它提供了對表單數據的全面控制,並簡化了表單輸入、驗證及提交邏輯。這裡我們從 useForm 中提取了 registerhandleSubmiterrors,這三個函數和對象是管理表單邏輯的關鍵。
  • register 用於註冊輸入框:我們通過 register 函數將表單元素(如 inputtextarea)與表單管理系統連接。這樣 react-hook-form 就可以跟蹤這些元素的狀態並進行驗證。
  • 表單驗證邏輯:每個輸入框都可以附帶驗證條件,如 requiredpattern 等。這樣當用戶提交表單時,react-hook-form 會自動檢查這些條件是否滿足,如果不滿足,則在 errors 對象中記錄相應的錯誤資訊。
  • 性能優化:由於 react-hook-form 是基於非受控組件的,React 的狀態不會在每次輸入變動時被更新。相比於使用 useState 管理表單輸入,這種方式顯著減少了重渲染次數,提高了應用的響應速度。

Step 3: 整合聯絡資訊與表單的多語言展示

這部分展示了如何在網頁上整合聯絡資訊(如營業時間、聯絡方式)與嵌入的表單,並使用 i18n 進行多語言處理。這樣可以實現根據使用者語言動態顯示不同語言的內容,提升國際化的支持。

import React from 'react';
import { useTranslation } from 'react-i18next';
import * as styles from './ContactSection.module.scss';
import ContactForm from '@/components/Form/ContactForm';

const ContactSection = ({ id }) => {
    const { t } = useTranslation();  // 引入翻譯函數

    return (
        <div id={id} className={styles.subcontainer}>
            <div className={styles.infoSection}>
                <h2 className={styles.title}>{t('collaborate.title')}</h2>
                <p className={styles.description}>
                    {t('collaborate.description')}
                </p>
                <div className={styles.infoBox}>
                    <div className={styles.infoItem}>
                        <img src={require(`@/assets/icons/Open.svg`)} alt="Open" className={styles.icon} />
                        <div>
                            <div className={styles.infoTitle}>{t('collaborate.businessHours')}</div>
                            <div className={styles.description}>{t('collaborate.hours')}</div>
                        </div>
                    </div>
                    <div className={styles.infoItem}>
                        <img src={require(`@/assets/icons/Newsletter.svg`)} alt="Email" className={styles.icon} />
                        <div>
                            <div className={styles.infoTitle}>{t('collaborate.email')}</div>
                            <div className={styles.description}>carol.cheng@luma-c.com</div>
                        </div>
                    </div>
                    <div className={styles.infoItem}>
                        <img src={require(`@/assets/icons/LINE.svg`)} alt="Chat" className={styles.icon} />
                        <div>
                            <div className={styles.infoTitle}>{t('collaborate.lineId')}</div>
                            <div className={styles.description}>@430trfzv</div>
                        </div>
                    </div>
                </div>
            </div>
            <ContactForm /> {/* 嵌入表單組件 */}
        </div>
    );
}

export default ContactSection;

這裡,我們展示了聯絡資訊(如營業時間、聯絡信箱和 LINE ID),並以多語言格式呈現,用戶根據不同語言環境能看到本地化的聯絡資訊描述。

Step 4: 測試表單驗證

現在表單具備了基本的驗證功能,你可以進行以下幾項測試來確保表單正常運作:

  1. 驗證必填欄位:當「Name」、「Email」或「Project Details」欄位未填寫時,表單應該顯示錯誤訊息。
  2. Email 格式驗證:輸入不正確的 Email 格式(如 abc@)時,表單應該提示 Email 格式錯誤。
  3. Reset 表單:表單提交成功後,應自動清空所有欄位,讓使用者能夠重新填寫。

結語

react-hook-form 不僅為我們提供了一個輕量級、高效能的表單管理方案,還通過簡潔的 API 幫助我們輕鬆應對複雜表單的驗證與狀態管理。從基本的輸入驗證到異步驗證,甚至是多步驟表單,react-hook-form 皆能靈活應對。

如果你正在尋找一個能顯著提升表單效能的工具,不妨試試 react-hook-form。在下一篇文章中,我們將進一步探討如何將表單數據提交到後端 API,並處理表單提交的狀態與回應。


流光館Luma<∕> ✨ 期待與你繼續探索更多技術知識!


上一篇
Day 26: 如何在 React 中追蹤用戶行為
下一篇
Day 28: 無需後端,利用 EmailJS 輕鬆處理表單郵件
系列文
從PM到前端開發:我的React作品集之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言